package com.wanjia.bpsapi.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.wanjia.bpsapi.constant.MapServiceRoute;
import com.wanjia.bpsapi.exception.MapServiceException;
import com.wanjia.bpsapi.pojo.dto.BaiduDrivingRoutePlanningResponse;
import com.wanjia.bpsapi.pojo.dto.PlaceSuggestionResponse;
import com.wanjia.bpsapi.properties.MapServiceProperties;
import com.wanjia.bpsapi.service.IMapService;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestTemplate;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Slf4j
public class BaiduMapServiceImpl implements IMapService {

    private MapServiceProperties properties;

    @Setter(onMethod_ = @Autowired)
    private RestTemplate restTemplate;

    private Map<String, Integer> codeMap;

    @Autowired
    public void setProperties(MapServiceProperties properties) throws IOException {
        this.properties = properties;
        parseCodeMapFile(properties.getBaidu().getCodeMapFile());
    }

    private void parseCodeMapFile(ClassPathResource codeMapFile) throws IOException {
        codeMap = new HashMap<>();
        try (InputStream in = codeMapFile.getInputStream()) {
            BufferedReader reader = IoUtil.getUtf8Reader(in);
            // 跳过首行
            reader.readLine();

            // 遍历解析
            int i = 0;
            String line;
            Pattern pattern = Pattern.compile("^(?<areaId>\\d+),(?<name>.*(地区|市|自治州|自治县|自治区|县|特别行政区|林区|群岛|盟))$");
            while ((line = reader.readLine()) != null) {
                i++;
                Matcher matcher = pattern.matcher(line);
                if (!matcher.matches()) {
                    log.error("第{}行地图配置解析异常: {}", i, line);
                    continue;
                }

                Integer areaId = Integer.parseInt(matcher.group("areaId"));
                String name = matcher.group("name");
                log.debug("解析地图配置: {}-{}", areaId, name);
                codeMap.put(name, areaId);
                codeMap.put(name.replaceAll("(地区|市|自治州|自治县|自治区|县|特别行政区|林区|群岛|盟)", ""), areaId);
            }
        }
    }

    @Override
    public Double distance(String origin, String destination) {
        String url = new StringBuilder()
                .append(properties.getBaseUrl())
                .append(properties.getRouteMap().get(MapServiceRoute.DRIVING_ROUTE_PLANNING))
                .append("?origin=")
                .append(origin)
                .append("&destination=")
                .append(destination)
                .append("&ak=")
                .append(properties.getBaidu().getAk())
                .toString();
        ResponseEntity<String> entity = restTemplate.getForEntity(url, String.class);
        if (entity.hasBody()) {
            BaiduDrivingRoutePlanningResponse response = JSONUtil.toBean(entity.getBody(), BaiduDrivingRoutePlanningResponse.class);
            if (response != null) {
                if (!Objects.equals(response.getStatus(), 0)) {
                    throw new MapServiceException();
                }

                return Optional.of(response)
                        .map(BaiduDrivingRoutePlanningResponse::getResult)
                        .map(BaiduDrivingRoutePlanningResponse.Result::getRoutes)
                        .orElseGet(ArrayList::new)
                        .stream()
                        .map(BaiduDrivingRoutePlanningResponse.Route::getDistance)
                        .min(Double::compareTo)
                        .orElse(null);
            }
        }
        return null;
    }

    @Override
    public String location(String name) {
        // 校验地址
        if (StrUtil.isBlank(name)) {
            return null;
        }

        // 检索行政区划
        String region = "";
        for (String key: codeMap.keySet()) {
            if (name.contains(key) & key.length() > region.length()) {
                region = key;
            }
        }
        if (StrUtil.isBlank(region)) {
            return null;
        }

        // 调用地址查询
        String url = new StringBuilder()
                .append(properties.getBaseUrl())
                .append(properties.getRouteMap().get(MapServiceRoute.PLACE_SUGGESTION))
                .append("?q=")
                .append(name)
                .append("&region=")
                .append(region)
                .append("&ak=")
                .append(properties.getBaidu().getAk())
                .append("&city_limit=true")
                .append("&output=json")
                .toString();
        ResponseEntity<String> entity = restTemplate.getForEntity(url, String.class);
        if (entity.hasBody()) {
            PlaceSuggestionResponse response = JSONUtil.toBean(entity.getBody(), PlaceSuggestionResponse.class);
            if (response != null) {
                if (!Objects.equals(response.getStatus(), 0)) {
                    throw new MapServiceException();
                }

                return Optional.of(response)
                        .map(PlaceSuggestionResponse::getResult)
                        .filter(CollUtil::isNotEmpty)
                        .orElseGet(ArrayList::new)
                        .stream()
                        .filter(result -> result.getLocation() != null)
                        .findFirst()
                        .map(result -> {
                            PlaceSuggestionResponse.Location location = result.getLocation();
                            if (location != null && location.getLat() != null && location.getLng() != null) {
                                String locationStr = location.getLat()+","+ location.getLng();
                                log.debug("检索地址为: {}[{}]", result.getName(), locationStr);
                                return locationStr;
                            } else {
                                log.error("检索结果异常,检索失败: {}", JSONUtil.toJsonStr(result));
                            }

                            return null;
                        })
                        .orElse(null);
            }
        }

        return null;
    }
}
